package log

import (
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strings"
	"time"
)

type LogLevel int

const (
	Info  LogLevel = 0
	Warn  LogLevel = 1
	Error LogLevel = 2
)

type LogType string

const (
	Console  LogType = "console"
	File     LogType = "file"
	DateFile LogType = "dateFile"
)

type Appender struct {
	Type     LogType
	FileName string
}

type Category struct {
	Appenders []string
	Leval     LogLevel
}

type Configure struct {
	Appenders  map[string]Appender
	Categories map[string]Category
}

type LogSucChan struct {
	out io.Writer
	str string
}

// 默认的配置
var _configure = Configure{
	Appenders:  make(map[string]Appender),
	Categories: make(map[string]Category),
}

var poolLog = NewPool(10)

func init() {
	// 默认规则
	_configure.Appenders["defalut"] = Appender{
		Type: "console",
	}
	_configure.Categories["default"] = Category{
		Appenders: []string{"default"},
		Leval:     Info,
	}
}

var logSucChan = make(chan LogSucChan, 100)
var logErrChan = make(chan error, 20)

func init() {
	go func() {
		for {
			select {
			case msg := <-logSucChan:
				msg.out.Write([]byte(msg.str))
			case err := <-logErrChan:
				{
					fmt.Println("[日志写入错误]", err.Error())
				}
			}
		}
	}()
}

func ConfigureLog(configure Configure) {
	for key, value := range configure.Appenders {
		_configure.Appenders[key] = value
	}

	for key, value := range configure.Categories {
		_configure.Categories[key] = value
	}
}

type Logger struct {
	category  Category
	Appenders map[string]Appender
	fileNames []string
}

func newLogger(category Category, appenders map[string]Appender, fileNames []string) *Logger {
	return &Logger{
		category:  category,
		Appenders: appenders,
		fileNames: fileNames,
	}
}

func fileExists(path string) bool {
	_, err := os.Stat(path)
	if err == nil {
		return true
	}
	if os.IsNotExist(err) {
		return false
	}
	return false
}

func handLog(str string, filenames []string, funcType string) {
	for _, filename := range filenames {
		go func(filename string) {
			str := fmt.Sprintf("[%s] [%s] [%s] %s",
				"自定义ginLog",
				time.Now().Format("2006-01-02 - 15:04:05"),
				funcType,
				str,
			)

			out := os.Stdout

			if filename != "" {
				// 需要文件流,先从池子里面取
				out = poolLog.Get(filename)

				// 不存在创建一个新的文件流
				if out == nil {
					dir := filepath.Dir(filename)
					if dir != "" && !fileExists(dir) {
						if err := os.MkdirAll(dir, 0755); err != nil {
							logErrChan <- err
							return
						}
					}

					newOut, err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)

					if err != nil {
						logErrChan <- err
						return
					}

					out = newOut
					poolLog.Set(filename, out)
				}
			}

			logSucChan <- LogSucChan{
				str: str,
				out: out,
			}
		}(filename)
	}
}

func (log *Logger) Info(str string) {
	handLog(str, log.fileNames, "Info")
}

func (log *Logger) Warn(str string) {
	if log.category.Leval < Warn {
		return
	}

	handLog(str, log.fileNames, "Warn")
}

func (log *Logger) Error(str string) {
	if log.category.Leval < Error {
		return
	}

	handLog(str, log.fileNames, "Error")
}

/*
 1、一个全局变量保存所有目录对应的文件流对象，每次GetLOgger
 的时候先生成对应的目录，然后判断对应的目录上是否存在对应的文件流对象
 没有的话就创建一个新的文件流到对象中

 2、保存文件流到全局对象的时候同时要保存文件流创建的时间，同时在使用的时候
 还要保存文件流的使用事件，并且这个全局对象还要支持配置最多保存多少个文件流对象，
 如果超出了，则将创建时间最久或者更新事件最久的文件流删除并且关闭。

 3、最后再启动一个定时器来定时检查文件流的使用情况，对于长时间未使用的文件流关闭并
 删除。
*/

func GetLogger(categoryKey string) *Logger {
	category, ok := _configure.Categories[categoryKey]

	if !ok {
		category = _configure.Categories["defalut"]
	}

	appenders := make(map[string]Appender)

	for _, key := range category.Appenders {
		appenders[key] = _configure.Appenders[key]
	}

	fileNames := []string{}
	for _, appender := range appenders {
		if appender.Type == Console {
			fileNames = append(fileNames, "")

		} else if (appender.Type == DateFile || appender.Type == File) && appender.FileName != "" {
			filename := appender.FileName

			// 给日志文件加上日期
			if appender.Type == DateFile {
				baseName := filepath.Base(filename)
				filename = strings.Replace(filename, baseName, "", 1)
				baseName = fmt.Sprintf("%s-%s", time.Now().Format("2006-01-02"), baseName)
				filename = fmt.Sprintf("%s%s", filename, baseName)
			}

			fileNames = append(fileNames, filename)
		}
	}

	return newLogger(category, appenders, fileNames)
}
